package com.yvan.leto.server.service;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.yvan.leto.SegmentHelper;
import com.yvan.leto.server.dao.RepositoryPersistent;
import com.yvan.platform.JsonWapper;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.val;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.dom.DOMDocument;
import org.dom4j.dom.DOMElement;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.lang.management.ManagementFactory;
import java.util.Collection;
import java.util.List;
import java.util.Map;

@Component(ConfigParserService.PARSER_PREFIX + SegmentHelper.LOGBACK)
public class LogbackParser implements IParser {

    private static final String PROCESS_ID_KEY = "PROCESS_ID";
    private static final String APP_NAME_KEY = "APP_NAME";
    private static final String PROPERTY_KEY = "property";
    private static final String OTHER_KEY = "other";
    private static final String APPENDER_KEY = "appender";
    private static final String LOGGER_KEY = "logger";
    private static final String ROOT_KEY = "root";

    @Autowired
    private RepositoryPersistent repositoryPersistent;

    @Override
    public String process(ParserContext context) {
        //合并所有 Resource
        List<File> resourceList = Lists.newArrayList();
        for (val relativePath : context.getSegment().getConfigFiles()) {
            resourceList.addAll(repositoryPersistent.repositoryEnvFileList(context.getEnv(), relativePath));
        }

        try {
            return mergeLogbackConfigurationString(resourceList, false, context.getProject().getName());

        } catch (Exception e) {
            return new JsonWapper().set("success", false).set("msg", e.getMessage()).toString(false);
        }
    }

    // 传入 spring-resource 描述的 location 集合, 执行对应的日志配置
    @SneakyThrows
    public String mergeLogbackConfigurationString(List<File> resourceList, boolean isDebug, String appName) {

        Document t = new DOMDocument();
        Element configuration = new DOMElement("configuration");
        if (isDebug) {
            configuration.addAttribute("debug", "true");
        }
        t.setRootElement(configuration);

        Map<String, Node> nodeProperty = Maps.newLinkedHashMap();
        Map<String, Node> nodeAppender = Maps.newLinkedHashMap();
        Map<String, Node> nodeLogger = Maps.newLinkedHashMap();
        Node nodeRoot = null;
        List<Node> nodeOther = Lists.newArrayList();

        Map<String, String> maps = setupLogbackProperties(appName);
        for (Map.Entry<String, String> entry : maps.entrySet()) {
            DOMElement element = new DOMElement(PROPERTY_KEY);
            element.setAttribute("name", entry.getKey());
            element.setAttribute("value", entry.getValue());
            nodeProperty.put(entry.getKey(), element);
        }

        for (val f : resourceList) {
            SAXReader saxReader = new SAXReader();

            @Cleanup InputStream is = new FileInputStream(f);
            org.dom4j.Document source = saxReader.read(is);
            for (org.dom4j.Element element : (List<Element>) source.getDocument().getRootElement().elements()) {
                if (element.getName().equalsIgnoreCase(PROPERTY_KEY)) {
                    nodeProperty.put(element.attributeValue("name"), element.detach());

                } else if (element.getName().equalsIgnoreCase(APPENDER_KEY)) {
                    nodeAppender.put(element.attributeValue("name"), element.detach());

                } else if (element.getName().equalsIgnoreCase(LOGGER_KEY)) {
                    nodeLogger.put(element.attributeValue("name"), element.detach());

                } else if (element.getName().equalsIgnoreCase(ROOT_KEY)) {
                    nodeRoot = element.detach();

                } else {
                    nodeOther.add(element.detach());

                }
            }
        }

        addNodeToDocument(nodeProperty.values(), configuration);
        addNodeToDocument(nodeOther, configuration);
        addNodeToDocument(nodeAppender.values(), configuration);
        addNodeToDocument(nodeLogger.values(), configuration);
        if (nodeRoot != null) {
            configuration.add(nodeRoot);
        }

        //return t.asXML();
        OutputFormat xmlFormat = new OutputFormat();
        xmlFormat.setEncoding("UTF-8");
        xmlFormat.setNewlines(true);
        xmlFormat.setIndent(true);
        xmlFormat.setIndent("    ");

        @Cleanup StringWriter stringWriter = new StringWriter();
        @Cleanup XMLWriter xmlWriter = new XMLWriter(stringWriter, xmlFormat);
        xmlWriter.write(t);
        return stringWriter.toString();
    }


    private void addNodeToDocument(Collection<Node> nodes, Element configuration) {
        if (nodes == null || nodes.size() <= 0)
            return;
        for (Node node : nodes) {
            configuration.add(node);
        }
    }

    private Map<String, String> setupLogbackProperties(String appName) {
        Map<String, String> r = Maps.newLinkedHashMap();
        r.put(PROCESS_ID_KEY, ManagementFactory.getRuntimeMXBean().getName());
        r.put(APP_NAME_KEY, appName);
        return r;
    }
}
